package org.jugile.jsontmpl.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;
import org.jugile.util.*;

import org.jugile.jsontmpl.common.*;



public abstract class ValueBase extends Entity {
	static Logger log = Logger.getLogger(ValueBase.class);
	
	public DOH getDoh() { return new DOH("Value",getId()); }

	public void reindex(Domain d) {}
	
	// --- fields ---

	private int ordnum;
	public int getOrdnum() { return ordnum; }
	public Value setOrdnum(int v) { ordnum = v; d()._modified(this); return (Value)this; }
 	private String value;
	public String getValue() { return value; }
	public Value setValue(String v) { value = v; d()._modified(this); return (Value)this; }
 	private FldType type;
	public FldType getType() { return type; }
	public Value setType(FldType v) { type = v; d()._modified(this); return (Value)this; }
  

 
	// --- connections ---

 
 
 

	// --- 1 - n ----

	private ChoiseCollection cChoises = new ChoiseCollection();
	public ChoiseCollection getChoises() { return cChoises.clone(); }
	public void addChoise(Choise o) { if (o != null) o.setValue((Value)this); }
	public void removeChoise(Choise o) { if (o != null) o.setValue(null); }
	protected void _addChoise(Choise o) { cChoises.add(o); }
	protected void _removeChoise(Choise o) { cChoises.remove(o); }
	public void addChoiseDistinct(Choise o) { if (!cChoises.contains(o)) cChoises.add(o); }
	

 

	// --- n - 1 ----

	private Fld fld;
	public Fld getFld() { return fld; }
	public Value setFld(Fld v) {
		d()._modified(this);
		return mapFld(v);
	}
	protected Value mapFld(Fld v) { 
		if (fld != null) fld._removeValue((Value)this);
		if (v != null) v._addValue((Value)this);
		fld = v;
		return (Value)this;
	}

	private Obj obj;
	public Obj getObj() { return obj; }
	public Value setObj(Obj v) {
		d()._modified(this);
		return mapObj(v);
	}
	protected Value mapObj(Obj v) { 
		if (obj != null) obj._removeValue((Value)this);
		if (v != null) v._addValue((Value)this);
		obj = v;
		return (Value)this;
	}

	private Array2 array2;
	public Array2 getArray2() { return array2; }
	public Value setArray2(Array2 v) {
		d()._modified(this);
		return mapArray2(v);
	}
	protected Value mapArray2(Array2 v) { 
		if (array2 != null) array2._removeValue((Value)this);
		if (v != null) v._addValue((Value)this);
		array2 = v;
		return (Value)this;
	}

	private Array2 array2Item;
	public Array2 getItemArray2() { return array2Item; }
	public Value setItemArray2(Array2 v) {
		d()._modified(this);
		return mapItemArray2(v);
	}
	protected Value mapItemArray2(Array2 v) { 
		if (array2Item != null) array2Item._removeItemValue((Value)this);
		if (v != null) v._addItemValue((Value)this);
		array2Item = v;
		return (Value)this;
	}

 

	// --- n - n ----
 
	// --- life cycle methods ---
	public void delete() { delete(true); }
	public void delete(boolean ondelete) {
		if (ondelete) this.onDelete();
 		Fld o1 = getFld();
		if (o1 != null) o1.removeValue((Value)this);  
		Obj o2 = getObj();
		if (o2 != null) o2.removeValue((Value)this);  
		Array2 o3 = getArray2();
		if (o3 != null) o3.removeValue((Value)this);  
		Array2 o4 = getItemArray2();
		if (o4 != null) o4.removeItemValue((Value)this);  
 		for (Choise o : getChoises()) {
			o.setValue(null);
		}
  		d().deleteValue(getId());
 		d()._deleted(this);
	}

	public void onDelete() {} // override 
	public void onLoad(IDomain d) {} // override
	public void onWrite() {} // override
	public void onDeleteEvent() {} // override
	public void onSave() {} // override - not implemented yet
	
	public String toString() {
		String res = "";
		res += nn(ordnum) + ";";
 		res += nn(value) + ";";
 		res += nn(type) + ";";
  		return res;
	}
	
	
	public final static String[] _flds = {
		"ordnum",
		"value",
		"type",
  		"Fld",
		"Obj",
		"Array2",
		"itemArray2",
 	};
	public static String[] flds() { return _flds; }
	
	public void parseOrdnum(String v) {
		setOrdnum(Jugile.parseIntSafe(v)); return;
 	}
	public void parseValue(String v) {
	    if ("-".equals(v)) v = null;
		setValue(v); return;
 	}
	public void parseType(String v) {
		if ("-".equals(v)) v = null;
		setType(FldType.map(v));
 	}
 
 
	public void parseFld(String id) {
		if ("-".equals(id)) setFld(null);
		else setFld( (Fld)d().getOrCreate("Fld", id));
	}
	public void parseObj(String id) {
		if ("-".equals(id)) setObj(null);
		else setObj( (Obj)d().getOrCreate("Obj", id));
	}
	public void parseArray2(String id) {
		if ("-".equals(id)) setArray2(null);
		else setArray2( (Array2)d().getOrCreate("Array2", id));
	}
	public void parseItemArray2(String id) {
		if ("-".equals(id)) setItemArray2(null);
		else setItemArray2( (Array2)d().getOrCreate("Array2", id));
	}
 
	// ----- db persistence -----

	protected static String _getSelectFlds() {
		Buffer buf = new Buffer();
		buf.add("id_f,ts,host,vers");
		for (String fld : _flds) {
			buf.add(","+fld+"_f");
		}
		return buf.toString();
	}
	protected String _getUpdateFlds() {
		Buffer buf = new Buffer();
		for (int i = 0; i < _flds.length; i++) {
			if (i > 0) buf.add(",");
			buf.add(_flds[i]+"_f=?");
		}
		return buf.toString();		
	}
	
	protected void _readFlds(Domain d, List row) {
		_modified = row.get(1)==null?null:new Time((java.util.Date)row.get(1));
		String host = (String)row.get(2);
		setVersion((Integer)row.get(3));
		ordnum = row.get(4)==null?0:(Integer)row.get(4);
		value = (String)row.get(5);
		type = row.get(6) == null? FldType.map(0):FldType.map((Integer)row.get(6));
  		mapFld(row.get(7)==null?null:Fld.getOrCreate(d,(Integer)row.get(7)));
		mapObj(row.get(8)==null?null:Obj.getOrCreate(d,(Integer)row.get(8)));
		mapArray2(row.get(9)==null?null:Array2.getOrCreate(d,(Integer)row.get(9)));
		mapItemArray2(row.get(10)==null?null:Array2.getOrCreate(d,(Integer)row.get(10)));
 
		// call onLoad
		onLoad(d);
		setVirgin(false);
	}

	
	protected void _writeFlds(DBConnection c) throws Exception {
		onWrite();
		// is it insert or update?
		if (isVirgin()) {
			c.prepare("insert into "+ _tablename+" set id_f=?,host=?,vers=?," + _getUpdateFlds());
			c.param(Long.parseLong(getId()));
			c.param(Domain.getHostName());
			c.param(1L);

			setWriteParams(c);
			
			try {
				c.execute();
			} catch(Exception se) {
				log.error("Failed to insert writeFlds Value: " + getId());
				throw se;
			}
			setVersion(1L);
		} else {
			c.prepare("update "+ _tablename+" set host=?,vers=?," + _getUpdateFlds() + " where id_f=? AND vers=?");
			c.param(Domain.getHostName());
			c.param(getVersion()+1L); // increment db version
			
			setWriteParams(c);
			
			c.param(Long.parseLong(getId()));
			c.param(getVersion());
			try {
				int res = c.execute();
				if (res != 1) {
					fail("object not found: Value " + getId() + " " + getVersion());
				}
			} catch(Exception se) {
				log.error("Failed to update writeFlds Value: " + getId());
				throw se;
			}
			// increment mem object versions when db commit is ok
		}
	}

	private void setWriteParams(DBConnection c) throws Exception {
		c.param(getOrdnum());
		c.param(trim(getValue(),300));
		c.param(getType());
  		c.param(getFld());
		c.param(getObj());
		c.param(getArray2());
		c.param(getItemArray2());
 	}

	private final static String _tablename = "value_t";
	protected String _getTableName() { return _tablename; }

	protected static int _readAllUpdates(Domain d, DBConnection c, Time ts, String host) throws Exception {
		if (host == null) {
			c.prepare("select "+Value._getSelectFlds()+" from "+_tablename+" where ts >= ?");			
			c.param(ts);
		} else {
			c.prepare("select "+Value._getSelectFlds()+" from "+_tablename+" where ts >= ? AND host != ?");			
			c.param(ts);
			c.param(host);
		}
		int count = 0;
		for (List row : c.select()) {
			long id = (Integer)row.get(0);
			Value o = getOrCreate(d,id);
			o._readFlds(d,row);
			count++;
		}
		return count;
	}
	
	private Time _modified;
	public Time getModifiedTime() { return _modified; }
	
	protected static Value getOrCreate(Domain d, long id) {
		Value o = d.getValue(id);
		if (o == null) o = d._createEmptyValue(id);
		return o;
	}

}